/*
* Copyright (c) 2025 Huawei Device Co., Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including but not limited to the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to provide persons with access to the Software
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGE OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

import {
  BreakpointRange,
  Breakpoint,
  BreakpointCallback,
  BreakpointValue,
  AspectRatioThreshold
} from '../../types/breakpoint';

/**
 * @description H5断点能力类封装
 * @param customBreakpoints 自定义断点属性和范围
 */
export class BreakpointManager {
  private _breakpoints: BreakpointRange<number>;
  private _defaultBreakpoint: (keyof BreakpointRange<BreakpointValue>)[];
  private _currentBreakpoint: Breakpoint;
  private _currentVerticalBreakpoint: Breakpoint;
  private _widthCallbacks: Set<BreakpointCallback>;
  private _heightCallbacks: Set<BreakpointCallback>;
  private _widthSubscribeStatus: boolean;
  private _heightSubscribeStatus: boolean;

  constructor(customBreakpoints?: BreakpointRange<number>) {
    this._breakpoints = customBreakpoints || this._getCustomBreakpoint() || {
      xs: 0,
      sm: 320,
      md: 600,
      lg: 840,
      xl: 1440,
    };
    this._defaultBreakpoint = ['xs', 'sm', 'md', 'lg', 'xl', 'xxl'];
    this._currentBreakpoint = 'sm';
    this._currentVerticalBreakpoint = 'sm';
    this._widthCallbacks = new Set();
    this._heightCallbacks = new Set();
    this._widthSubscribeStatus = true;
    this._heightSubscribeStatus = true;
    this._updateBreakpoint = this._updateBreakpoint.bind(this);
    this._updateBreakpoint();
    window.addEventListener('resize', this._updateBreakpoint);
  }

  private _getCustomBreakpoint() {
    if ((window as any).adaptiveUiConfig) {
      return (window as any).adaptiveUiConfig.breakpoint ? (window as any).adaptiveUiConfig.breakpoint : null;
    } else {
      return null;
    }
  }

  private _updateBreakpoint(): void {
    const width = window.innerWidth;
    const height = window.innerHeight;
    const aspectRatio  = height / width; //计算高宽比
    
    let newBreakpoint: Breakpoint = 'sm';

    for (const [key, value] of Object.entries(this._breakpoints)) {
      if (width >= value) {
        newBreakpoint = key as Breakpoint;
      } else {
        break;
      }
    }

    let newVerticalBreakpoint: Breakpoint;
    if (aspectRatio < AspectRatioThreshold.SM) {
      newVerticalBreakpoint = 'sm';
    } else if (aspectRatio >= AspectRatioThreshold.SM && aspectRatio < AspectRatioThreshold.MD) {
      newVerticalBreakpoint = 'md';
    } else {
      newVerticalBreakpoint = 'lg';
    }

    if (newBreakpoint !== this._currentBreakpoint && this._widthSubscribeStatus) {
      this._currentBreakpoint = newBreakpoint;
      this._notifyWidthCallbacks();
    }
    if (newVerticalBreakpoint !== this._currentVerticalBreakpoint && this._heightSubscribeStatus) {
      this._currentVerticalBreakpoint = newVerticalBreakpoint;
      this._notifyHeightCallbacks();
    }
  }

  private _notifyWidthCallbacks(): void {
    this._widthCallbacks.forEach((callback) => callback(this._currentBreakpoint));
  }

  private _notifyHeightCallbacks(): void {
    this._heightCallbacks.forEach((callback) => callback(this._currentVerticalBreakpoint));
  }

  public subscribeToBreakpoint(widthCallback: BreakpointCallback|null = null, heightCallback: BreakpointCallback|null = null): () => void {
    this._widthSubscribeStatus = false;
    this._heightSubscribeStatus = false;
    if (widthCallback) {
      this._widthSubscribeStatus = true;
      this._widthCallbacks.add(widthCallback);
      widthCallback(this._currentBreakpoint);
    }
    if (heightCallback) {
      this._heightSubscribeStatus = true;
      this._heightCallbacks.add(heightCallback);
      heightCallback(this._currentVerticalBreakpoint);
    }
    return () => {
      widthCallback === null || this._widthCallbacks.delete(widthCallback);
      heightCallback === null || this._heightCallbacks.delete(heightCallback);
    }
  }

  private _fillBreakpointValue(param: BreakpointRange<BreakpointValue>) {
    const result = { ...param };
    // 查找第一个和最后一个有值的索引
    const firstValueIndex = this._defaultBreakpoint.findIndex((key: Breakpoint) => param[key] !== undefined);
    const lastValueIndex = this._defaultBreakpoint.findLastIndex((key: Breakpoint) => param[key] !== undefined);

    // 向左填充
    for (let i = firstValueIndex - 1; i >= 0; i--) {
      result[this._defaultBreakpoint[i]] = param[this._defaultBreakpoint[firstValueIndex]];
    }

    // 向右填充
    for (let i = lastValueIndex + 1; i < this._defaultBreakpoint.length; i++) {
      result[this._defaultBreakpoint[i]] = param[this._defaultBreakpoint[lastValueIndex]];
    }
    return result as Required<BreakpointRange<BreakpointValue>>;
  }

  public useBreakpointValue(values: BreakpointRange<BreakpointValue>): any {
    const filledValues = this._fillBreakpointValue(values);
    if (this._currentBreakpoint && filledValues[this._currentBreakpoint] !== undefined) {
      return filledValues[this._currentBreakpoint];
    } else {
      return filledValues.sm;
    }
  }

  public useVerticalBreakpointValue(values: BreakpointRange<BreakpointValue>): any {
    const filledValues = this._fillBreakpointValue(values);
    if (this._currentVerticalBreakpoint && filledValues[this._currentVerticalBreakpoint] !== undefined) {
      return filledValues[this._currentVerticalBreakpoint];
    } else {
      return filledValues.sm;
    }
  }

  public getCurrentBreakpoint(): Breakpoint {
    return this._currentBreakpoint;
  }

  public getCurrentVerticalBreakpoint(): Breakpoint {
    return this._currentVerticalBreakpoint;
  }

  public getBreakpoints(): BreakpointRange<number> {
    return { ...this._breakpoints };
  }

  public destroy(): void {
    window.removeEventListener('resize', this._updateBreakpoint);
    this._widthCallbacks.clear();
    this._heightCallbacks.clear();
  }
}